import { AxiosRequestConfig, AxiosResponse } from 'axios';
import { Http, http, HttpConfig, HttpMethod, InstanceCallback } from './http';

const SymbolId = Symbol('id');
const SymbolHttp = Symbol('http');

export type HttpResponse<T> = AxiosResponse<T>;

export default class Api {
	private [SymbolId]: symbol;

	private [SymbolHttp]: Http;

	protected method: HttpMethod;

	constructor() {
		this[SymbolHttp] = http;
		this.method = http.method;
	}

	/**
	 * @author 戴俊明 <idaijunming@163.com>
	 * @description 初始化通用http请求配置，如果已被初始化，覆盖原有配置。该方法返回的实例可做链式调用。
	 * @date 2020/1/30 20:50
	 **/
	protected init(
		config?: AxiosRequestConfig,
		callback?: InstanceCallback
	): Api {
		this[SymbolId] = this.http.create(
			{
				id: Symbol.for('Api'),
				...config,
			},
			callback
		);
		return this;
	}

	/**
	 * @author 戴俊明 <idaijunming@163.com>
	 * @description 根据key初始化http请求配置，如果已被初始化，覆盖原有配置。该方法返回的实例可做链式调用。
	 * @date 2020/1/30 20:53
	 **/
	protected initSingle(
		key: string,
		config?: AxiosRequestConfig,
		callback?: InstanceCallback
	): Api {
		this[SymbolId] = this.http.create(
			{
				id: Symbol.for(key),
				...config,
			},
			callback
		);
		return this;
	}

	protected initSimpleInstance = (key: string): Api => {
		return this.initSingle(key, {
			baseURL: `/api/${key}`,
		}) as Api;
	};

	protected initInstance = (
		key: string,
		config?: AxiosRequestConfig,
		callback?: InstanceCallback
	): Api => {
		return this.initSingle(key, config, callback) as Api;
	};

	public destroy = (): boolean => this.http.destroy(this.id);

	public request = <T = any, R = AxiosResponse<T>>(
		config: AxiosRequestConfig
	): Promise<AxiosResponse<T>> => {
		const tmp: HttpConfig = {
			id: this.id,
			...config,
		};
		return this.http.request(tmp);
	};

	public all = <T>(
		configs: AxiosRequestConfig[]
	): Promise<AxiosResponse<T>[]> =>
		new Promise((resolve, reject) => {
			this.http
				.raw()
				.all(configs.map(config => this.request(config)))
				.then(
					this.http.raw().spread((...results) => {
						resolve(results);
					})
				)
				.catch(error => {
					reject(error);
				});
		});

	public allMethod = <T = any>(ApiMethods: Promise<T>[]): Promise<T[]> =>
		new Promise((resolve, reject) => {
			this.http
				.raw()
				.all(ApiMethods)
				.then(
					this.http.raw().spread((...results) => {
						resolve(results);
					})
				)
				.catch(error => {
					reject(error);
				});
		});

	get id(): symbol {
		return this[SymbolId];
	}

	get http(): Http {
		return this[SymbolHttp];
	}
}
